import { getSw8, InterceptFetchType } from "@/core";

type Options = Omit<InterceptFetchType, "pagePath" | "appId"> & {
  method?: string;
};

type XHR = XMLHttpRequest & { body?: any };

function addListeners(xhr: XHR, { method, onError, onBefore, onAfter }: Options) {
  const handleError = (e: ProgressEvent) => {
    const res = e.target as XMLHttpRequest;
    onError &&
      onError({
        url: res.responseURL,
        status: res.status,
        statusText: res.statusText,
        method: method!,
        body: xhr.body,
        elapsedTime: e.loaded,
      });
  };
  xhr.addEventListener("loadstart", (e: ProgressEvent) => {
    const res = e.target as XMLHttpRequest;
    onBefore &&
      onBefore({
        url: res.responseURL,
        method: res.response.method,
        options: res.getAllResponseHeaders(),
      });
  });
  xhr.addEventListener("load", (e: ProgressEvent) => {
    const res = e.target as XMLHttpRequest;
    if (!(res.status >= 200 && res.status < 300)) {
      handleError(e);
    }
  });
  xhr.addEventListener("loadend", (e: ProgressEvent) => {
    const res = e.target as XMLHttpRequest;
    onAfter && onAfter(res.response);
  });
  xhr.addEventListener("error", handleError);
  xhr.addEventListener("abort", handleError);
  xhr.addEventListener("timeout", handleError);
}

export const interceptXHR = ({ appId, pagePath, onError, onBefore, onAfter }: InterceptFetchType) => {
  const open = XMLHttpRequest.prototype.open;
  const send = XMLHttpRequest.prototype.send;
  XMLHttpRequest.prototype.open = function (method: string, url: string | URL, ...args: any[]) {
    let uri: URL;
    if (typeof url === "string") {
      if (/^http(s):\/\//.test(url)) {
        uri = new URL(url);
      } else if (url.startsWith("//")) {
        uri = new URL(`${globalThis.location.protocol}${url}`);
      } else {
        uri = new URL(globalThis.location.href);
        uri.pathname = url;
      }
    } else {
      uri = url;
    }
    addListeners(this, {
      method,
      onError,
      onBefore,
      onAfter,
    });
    // @ts-ignore
    open.call(this, method, url, ...args);
    this.setRequestHeader(
      "sw8",
      getSw8({
        appId: appId,
        herf: uri.href,
        pagePath,
      })
    );
  };
  XMLHttpRequest.prototype.send = function (body) {
    (this as XHR).body = body;
    send.call(this, body);
  };
  return XMLHttpRequest;
};
